using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Practices.Unity.Interception
{
    [TestClass]
    public class InParametersVirtualInterceptorTest
    {
        static T WrapAndCreateType<T>(IEnumerable<KeyValuePair<MethodBase, List<IInterceptionHandler>>> handlers,
                                      params object[] ctorArgs)
        {
            Type wrappedType = VirtualInterceptor.WrapClass(typeof(T));
            ILEmitProxy proxy = new ILEmitProxy(handlers);
            List<object> wrappedCtorArgs = new List<object> { proxy };
            wrappedCtorArgs.AddRange(ctorArgs);
            return (T)Activator.CreateInstance(wrappedType, wrappedCtorArgs.ToArray());
        }

        [TestMethod]
        public void OneParameter()
        {
            Recorder.Records.Clear();
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(SpyIn).GetMethod("OneParameter");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            SpyIn result = WrapAndCreateType<SpyIn>(dictionary);
            int retValue = result.OneParameter(21);

            Assert.AreEqual(21 * 2, retValue);
            Assert.AreEqual(3, Recorder.Records.Count);
            Assert.AreEqual("Before Method", Recorder.Records[0]);
            Assert.AreEqual("In Method", Recorder.Records[1]);
            Assert.AreEqual("After Method", Recorder.Records[2]);
        }

        [TestMethod]
        public void TwentyParameters()
        {
            Recorder.Records.Clear();
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(SpyIn).GetMethod("TwentyParameters");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            SpyIn result = WrapAndCreateType<SpyIn>(dictionary);
            int retValue = result.TwentyParameters(12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10);

            Assert.AreEqual(120, retValue);
            Assert.AreEqual(3, Recorder.Records.Count);
            Assert.AreEqual("Before Method", Recorder.Records[0]);
            Assert.AreEqual("In Method", Recorder.Records[1]);
            Assert.AreEqual("After Method", Recorder.Records[2]);
        }

        [TestMethod]
        public void TwoParameters()
        {
            Recorder.Records.Clear();
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(SpyIn).GetMethod("TwoParameters");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            SpyIn result = WrapAndCreateType<SpyIn>(dictionary);
            string retValue = result.TwoParameters(42, "Hello ");

            Assert.AreEqual("Hello 42", retValue);
            Assert.AreEqual(3, Recorder.Records.Count);
            Assert.AreEqual("Before Method", Recorder.Records[0]);
            Assert.AreEqual("In Method", Recorder.Records[1]);
            Assert.AreEqual("After Method", Recorder.Records[2]);
        }

        public class SpyIn
        {
            public virtual int OneParameter(int x)
            {
                Recorder.Records.Add("In Method");
                return x * 2;
            }

            public virtual int TwentyParameters(int p0,
                                                int p1,
                                                int p2,
                                                int p3,
                                                int p4,
                                                int p5,
                                                int p6,
                                                int p7,
                                                int p8,
                                                int p9,
                                                int p10,
                                                int p11,
                                                int p12,
                                                int p13,
                                                int p14,
                                                int p15,
                                                int p16,
                                                int p17,
                                                int p18,
                                                int p19)
            {
                Recorder.Records.Add("In Method");
                return p0 * p19;
            }

            public virtual string TwoParameters(int x,
                                                string y)
            {
                Recorder.Records.Add("In Method");
                return y + x;
            }
        }
    }
}